//
//  NetworkMonitor.m
//  InstantUang
//
//  Created by Bin on 2024/12/12.
//

#import "NetworkMonitor.h"
#import <netinet/in.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>

@interface NetworkMonitor ()

@property (nonatomic, strong) CTTelephonyNetworkInfo *telephonyNetworkInfo;
@property (nonatomic, copy) NSString *currentRadioAccessTechnology;

@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;

@end

@implementation NetworkMonitor

+ (instancetype)sharedMonitor {
    static NetworkMonitor *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init];
        _currentStatus = NetworkStatusUnknown;
        _currentStatusString = @"未知网络";
    }
    return self;
}

- (void)dealloc {
    [self stopMonitoring];
}

- (void)startMonitoringWithBlock:(NetworkStatusBlock)block {
    self.networkStatusBlock = block;
    
    // 监听蜂窝网络变化
    [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(radioAccessTechnologyDidChange:)
                                                 name:CTServiceRadioAccessTechnologyDidChangeNotification
                                             object:nil];
    
    // 初始化reachability
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;
    
    _reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
    
    SCNetworkReachabilityContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
    SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context);
    SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    
    // 立即检查一次
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
            [self reachabilityChanged:flags];
        }
    });
}

- (void)stopMonitoring {
    if (_reachabilityRef) {
        SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        CFRelease(_reachabilityRef);
        _reachabilityRef = NULL;
    }
    
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:CTServiceRadioAccessTechnologyDidChangeNotification
                                                object:nil];
}

static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
    @autoreleasepool {
        NetworkMonitor *monitor = (__bridge NetworkMonitor *)info;
        [monitor reachabilityChanged:flags];
    }
}

- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags {
    NetworkStatus status = NetworkStatusUnknown;
    
    if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
        status = NetworkStatusNotReachable;
    }
    
    if (status != NetworkStatusNotReachable) {
        if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
            status = NetworkStatusWiFi;
        }
        
        if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) ||
             (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
            if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
                status = NetworkStatusWiFi;
            }
        }
    }
    
    if (status == NetworkStatusUnknown) {
        // 可能是蜂窝网络
        status = [self getWWANStatus];
    }
    
    _currentStatus = status;
    _currentStatusString = [self networkStatusString:status];
    
    if (self.networkStatusBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.networkStatusBlock(status);
        });
    }
}

- (void)radioAccessTechnologyDidChange:(NSNotification *)notification {
    SCNetworkReachabilityFlags flags;
    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) {
        [self reachabilityChanged:flags];
    }
}

- (NetworkStatus)getWWANStatus {
    NSString *currentRadioAccessTechnology = nil;
    
    if (@available(iOS 12.0, *)) {
        NSDictionary *radioDict = self.telephonyNetworkInfo.serviceCurrentRadioAccessTechnology;
        if (radioDict.count > 0) {
            currentRadioAccessTechnology = radioDict.allValues.firstObject;
        }
    }
    
    if (currentRadioAccessTechnology == nil) {
        return NetworkStatusUnknown;
    }
    
    static NSDictionary *networkStatusMap;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        networkStatusMap = @{
            CTRadioAccessTechnologyGPRS: @(NetworkStatusWWAN2G),
            CTRadioAccessTechnologyEdge: @(NetworkStatusWWAN2G),
            CTRadioAccessTechnologyCDMA1x: @(NetworkStatusWWAN2G),
            CTRadioAccessTechnologyWCDMA: @(NetworkStatusWWAN3G),
            CTRadioAccessTechnologyHSDPA: @(NetworkStatusWWAN3G),
            CTRadioAccessTechnologyHSUPA: @(NetworkStatusWWAN3G),
            CTRadioAccessTechnologyCDMAEVDORev0: @(NetworkStatusWWAN3G),
            CTRadioAccessTechnologyCDMAEVDORevA: @(NetworkStatusWWAN3G),
            CTRadioAccessTechnologyCDMAEVDORevB: @(NetworkStatusWWAN3G),
            CTRadioAccessTechnologyeHRPD: @(NetworkStatusWWAN3G),
            CTRadioAccessTechnologyLTE: @(NetworkStatusWWAN4G),
            
            // iOS 14+ 5G支持
            CTRadioAccessTechnologyNRNSA: @(NetworkStatusWWAN5G),
            CTRadioAccessTechnologyNR: @(NetworkStatusWWAN5G)
        };
    });
    
    NSNumber *statusNumber = networkStatusMap[currentRadioAccessTechnology];
    if (statusNumber) {
        return [statusNumber integerValue];
    }
    
    return NetworkStatusUnknown;
}

- (NSString *)networkStatusString:(NetworkStatus)status {
    switch (status) {
        case NetworkStatusNotReachable: return @"无网络";
        case NetworkStatusWWAN2G:      return @"2G";
        case NetworkStatusWWAN3G:      return @"3G";
        case NetworkStatusWWAN4G:      return @"4G";
        case NetworkStatusWWAN5G:      return @"5G";
        case NetworkStatusWiFi:        return @"WiFi";
        default:                       return @"未知网络";
    }
}

@end
